#ifndef __SOCKET_H__
#define __SOCKET_H__

#include "mrc_base.h"

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

/**

  \defgroup socket Socket Managment
  
  @section socket_intro Introduction 
  
  Socket Managment is just a wrapper module of the platorm socket APIs. It is desinged to fix
  the issues below: 
  
  - Send data to a Socket in block(or synchronous) way with platorm APIs will cause system 
  	blocking. For our application just run in a single thread that called MMI thread, the system
  	blocking happen in this situation:Application send data with platform api in a loop, but the 
  	network send buffer is full, the code just look like this:
	\code
		while (size > 0 && (sendbytes = mrc_send(socket, buffer, size)) >= 0)
		{
			size -= sendbytes;
		}
	\endcode
	When mrc_send return 0 from network layer to tell the the application that network is busy
	or send buffer full, 	but the loop resend again and again. This cause the system do not 
	has the time slice to handle the network layer works. So the block happen. Even if Network
	and MMI run in separate threads, the unblock(or asynchronous) way will lead to short 
	response time to MMI events.
	
  - Release the Engineer from the rewrite work of the same code.
	This module provide a simple interface for the engineer. Just a event handler is needed
	for a socket. So the engineer can focus on the logic of the application protocol implements.

  @section socket_session Typical Session
  
  To work with Socket Managment Module:
  - Create your socket events handler.
  - Create the socket with your events handler.
  - Call Socket_Connect to connet the server (connect is not needed for UDP);
  - Call Socket_Send/Socket_SendTo to send the user data.

  Sample code:
  
  \code 
	// Your Events handler
	int32 UserSocketEventsHandler(PSOCKET socket, int32 evt, PSOCKEVTDATA data)
	{
		switch(evt)
		{
		case SOCKEVT_CONNECTED:
			//send the request, when close the socket here must return 1(non zero)
			break;

		case SOCKEVT_RECVDATA:
			//save the data, when close the socket here must return 1(non zero)
			break;
			
		case SOCKEVT_CONNECTFAILED:
		case SOCKEVT_ERROR:
			//error handler, maybe reconnect
			break;
			
		case SOCKEVT_CLOSED:
		default:
			//finished the use of the socket
			break;
		}

		return 0;
	}

	//Your code:
	socket = Socket_Create(SOCKPROTO_TCP, UserSocketEventsHandler, userdata);
	if(socket) 
		Socket_Connect(socket, ip, port, FALSE);
	...
  \endcode

  Engineer can send data to a TCP socket which not connected, and the send data 
  is kept in the send buffer, when socket connected the data will be sent. The code just look like this:

  \code
	socket = Socket_Create(SOCKPROTO_TCP, UserSocketEventsHandler, userdata);
	if(socket)
	{
	 	//Socket_Send(socket, buffer, size); // this also supported, but not suggested
	 	Socket_Connect(socket, ip, port, FALSE); //unblock way connect
	 	Socket_Send(socket, buffer, size);  //here the socket state is SOCKSTAT_CONNECTING
	 	...
	}
   \endcode
   
  @ingroup network
  @{
 */

/**
* \brief The max number of the sockets.
*
* This value should be reconfigured in different platform, and the value must be <= the value 
* of target platform supported. For Spreadtrum just only support 1 socket, the 1 is the only 
* value can be configured. MTK support 2 sockets, so 1, 2 are available;
*/
#ifndef SOCKET_MAX_COUNT
#define SOCKET_MAX_COUNT					2
#endif

/**
 * \brief The socket select interval value in ms.
 *
 * In socket module implement there is a timer, and all the works are driven by this timer.
 * For the system timer precision is 100ms, normally 200 - 300ms is the suggestion.
 */
#ifndef SOCKET_SELECT_INTERVAL
#define SOCKET_SELECT_INTERVAL				100  //200 
#endif

/**
 * \brief Receive buffer size in bytes.
 *
 * This buffer is a temp storage for receive data from network.
 * In the select timer we will try to read SOCKET_RECVBUFFER_SIZE bytes from the socket one time.
 */
#ifndef SOCKET_RECVBUFFER_SIZE
#ifdef LOW_MEMORY
#define SOCKET_RECVBUFFER_SIZE			3200 // 2*1024
#else
#define SOCKET_RECVBUFFER_SIZE			4600 // 2*1024
#endif
#define ONCE_RECV_LEN                                       4000 // 1500
#endif

typedef struct SOCKET_T* PSOCKET;

/**
 * \brief Socket protocol type.
 *
 * The socke support two protocol type tcp/udp. When tcp the data sent as a stream, when udp 
 * the data sent as diagram.
 */
typedef enum SOCKET_PROTOCOL_E
{
	//tcp
	SOCKPROTO_TCP, 

	//udp
	SOCKPROTO_UDP
}SOCKPROTO;

/**
 * \brief Socket notify events.
 *
 * Socket module will notify the user with these events when something happen,
 * and this is the only way socket module communicate with appliction code.
 */
typedef enum SOCKET_EVENT_E{

	/** sent when connect success with no event data */
	SOCKEVT_CONNECTED,
	
	/** sent when connecting failed with no event data */
	SOCKEVT_CONNECTFAILED,
	
	/** sent when anything error happen with no event data */
	SOCKEVT_ERROR,
	
	/** sent when the socket closed by user in normal case(finished use of the socket) with not event data*/
	SOCKEVT_CLOSED,

	SOCKEVT_DATASENT = SOCKEVT_CONNECTED+8,
	/** 
	* sent when receive data from network with event data
	* 
	* \code
	*	case SOCKEVT_RECVDATA:
	*		data->buffer; //pointer to the receive buffer
	*		data->size; //buffer size
	*		data->ip; //server ip address, udp only
	*		data->port; //server port, udp only
	* \endcode
	*/
	SOCKEVT_RECVDATA, 

	SOCKEVT_MAX
	
}SOCKEVENT;

/**
 * \brief State of socket.
 */
typedef enum SOCKET_STATE_E{

	/**  just after the socket created */
	SOCKSTAT_CREATED,

	/** on connecting to the server */
	SOCKSTAT_CONNECTING,

	/** connect successfully */
	SOCKSTAT_CONNECTED,

	/** closed on error or normal */
	SOCKSTAT_CLOSED
}SOCKSTAT;

/**
 * \brief Socket event handler.
 *
 * When create the socket, user should provide a event handler. In the handler user should 
 * implement the code to handle all the events application care about. When return no zero 
 * from the event handler, that means user do not want to do anything more on this socket.
 *
 * \param socket The Socket handle
 * \param evt The socket notify events
 * \param data the event data
 * \return 
 *	- 0 -- do not called Socket_Close on that socket in the event process
 *	- 1 -- called the Socket_Close in the event process
 *
 * Sample Code look like this:
 * \code
 *	case SOCKEVT_RECVDATA:
 *		...
 *		Socket_Close(socket, SOCKEVT_ERROR);
 *		return 1; //should return non zero
 *
 *	default: //other events
 *		return 0;
 * \endcode
 *
 * \sa SOCKET_EVENT_E
 */
typedef int32 (*FN_SOCKEVENT)(PSOCKET socket, int32 evt, void* data);

typedef struct {
	uint8 buf[SOCKET_RECVBUFFER_SIZE];
	int32 len;
	int32 maxLen;
} RECVBUF, *PRECVBUF;

typedef struct NETMSG_T {
	uint8* buf;
#ifdef SOCKET_UDP_ENABLE
	uint32 ip;
	uint16 port;
#endif
	int32 dataLen;
	int32 sentLen;
	int32 privData;
	struct NETMSG_T* next;
} NETMSG, *PNETMSG;

/**
 * \brief Socket struct.
 */
typedef struct SOCKET_T{
	/** platform socket descriptor */
	int32 sd;
	/** socket protoco type */
	SOCKPROTO protocol;
	/** socket state */
	SOCKSTAT state;
	/** send function, user can replace it */
	//FN_SOCKSEND fnSend;
	/** receive function, user can replace it */
	//FN_SOCKRECV fnRecv;
	/** user provided events handler */
	FN_SOCKEVENT fnEvent;
	//FN_SENTCB fnSentCb;
	/** waitting queue of send messag */
	PNETMSG sendList;
	/** recieve buffer */
	PRECVBUF recvBuf;
	/** user specific data */
	int32 userdata;
}SOCKET, *PSOCKET;

/**
 * \brief The socket event data only used on event SOCKEVT_RECVDATA.
 */
typedef struct SOCKEVTDATA_T
{
	/** receive data buffer */
	uint8* buffer;  
	/** receive data size */
	int32 size;
	/** server ip address, udp only */
	uint32 ip;
	/** server port number, udp only */
	uint16 port;
}SOCKEVTDATA, *PSOCKEVTDATA;


void Socket_Initialize(void);
void Socket_Terminate(void);
PSOCKET Socket_Create(SOCKPROTO protocol, FN_SOCKEVENT fnEvent, int32 userdata);
int32 Socket_Close(PSOCKET socket, SOCKEVENT evt);
int32 Socket_Connect(PSOCKET socket, uint32 ip, uint16 port, int block);
#ifdef SOCKET_UDP_ENABLE
int32 Socket_SendTo(PSOCKET socket, uint8* data, uint32 size, int32 ip, uint16 port);
#define Socket_Send(socket, data, size) Socket_SendTo(socket, data, size, 0, 0)
#else
int32 Socket_Send(PSOCKET socket, uint8* data, uint32 size);
#endif
int32 Socket_IsConnected(PSOCKET socket);
void Socket_CloseAll(SOCKEVENT evt);
int Socket_IsNull(PSOCKET socket);

#ifdef __cplusplus
}
#endif  /* __cplusplus */

#endif


